// This file is a part of Funky C++.
// Copyright (C) 2011  Valentin David
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program.  If not, see <http://www.gnu.org/licenses/>.
#ifndef COMPLEX_HH
# define COMPLEX_HH

# include <complex>
# include "signal.hh"

namespace funky {

  template <typename T>
  struct complex_of {
    typedef std::complex<T> type;
    static std::complex<T> convert(const T& in) {
      return std::complex<T>{in};
    }
  };

  template <typename T>
  struct complex_of<std::complex<T> > {
    typedef T type;
    static const std::complex<T>& convert(const std::complex<T>& in) {
      return in;
    }
    static const std::complex<T>& convert(std::complex<T>&& in) {
      return std::move(in);
    }
  };

  template <typename T, size_t N>
  struct complex_of<bus<T, N> > {
    typedef bus<typename complex_of<T>::type, N> type;
    static type convert(const bus<T, N>& in) {
      type ret;
      for (size_t i = 0; i < N; ++i) {
	ret[i] = in[i];
      }
      return ret;
    }
  };

  struct abs_op {
    template <typename T>
    auto operator()(T in) const ->
      decltype(std::abs(in))
    {
      return std::abs(in);
    }

    template <typename T, size_t N>
    auto operator()(bus<T, N> in) const ->
      bus<decltype(std::abs(std::declval<T>())), N>
    {
      bus<decltype(std::abs(std::declval<T>())), N> ret;
      for (size_t i = 0; i < N; ++i)
	ret[i] = std::abs(in[i]);
      return ret;
    }
  };

  struct imag_op {
    template <typename T>
    auto operator()(T in) const ->
      decltype(std::imag(in))
    {
      return std::imag(in);
    }
  };

  struct real_op {
    template <typename T>
    auto operator()(T in) const ->
      decltype(std::real(in))
    {
      return std::real(in);
    }
  };

  template <typename Op, typename Signal>
  struct abs_signal {
  private:
    typedef typename signal_traits<Signal>::sample sample;
    typedef typename signal_traits<Signal>::duration duration;

    Signal signal;
    Op op;

  public:
    explicit abs_signal(Signal&& signal, Op op = Op()):
      signal(std::move(signal)),
      op(op)
    {
    }

    explicit abs_signal(const Signal& signal, Op op = Op()):
      signal(signal),
      op(op)
    {
    }

    abs_signal(const abs_signal&) = default;
    abs_signal(abs_signal&& other)
      : signal(std::move(other.signal)),
	op(std::move(op)) {}

    abs_signal& operator=(const abs_signal& other) {
      return *this = abs_signal(other);
    }

    abs_signal& operator=(abs_signal&& other) throw() {
      std::swap(signal, other.signal);
      std::swap(op, other.op);
      return *this;
    }

    abs_signal& operator+=(const duration& d) {
      signal += d;
      return *this;
    }

    abs_signal& operator++() {
      ++signal;
      return *this;
    }

    abs_signal operator++(int) {
      abs_signal ret{*this};
      ++*this;
      return ret;
    }

    auto operator*() const -> decltype(op(*signal)) {
      return op(*signal);
    }

  };

  template <typename Op, typename T>
  struct signal_traits<abs_signal<Op, T> > {
    typedef decltype(*(std::declval<abs_signal<Op, T> >())) sample;
    typedef typename signal_traits<T>::duration duration;
  };


  template <typename T, typename RT = typename std::decay<T>::type,
	    typename = typename signal_traits<RT>::sample>
  abs_signal<abs_op, RT>
  abs(T&& t) {
    return abs_signal<abs_op, RT>{std::forward<T>(t)};
  }

  template <typename T, typename RT = typename std::decay<T>::type,
	    typename = typename signal_traits<RT>::sample>
  abs_signal<real_op, RT>
  real(T&& t) {
    return abs_signal<real_op, RT>{std::forward<T>(t)};
  }

  template <typename T, typename RT = typename std::decay<T>::type,
	    typename = typename signal_traits<RT>::sample>
  abs_signal<imag_op, RT>
  imag(T&& t) {
    return abs_signal<imag_op, RT>{std::forward<T>(t)};
  }

  template <typename Signal>
  struct complex_signal {
  private:
    typedef typename complex_of<typename signal_traits<Signal>
				::sample>::type sample;
    typedef typename signal_traits<Signal>::duration duration;
    Signal signal;

  public:
    explicit complex_signal(Signal&& signal):
      signal(std::move(signal))
    {
    }

    explicit complex_signal(const Signal& signal):
      signal(signal)
    {
    }

    complex_signal(const complex_signal&) = default;
    complex_signal(complex_signal&& other)
      : signal(std::move(other.signal)) {}

    complex_signal& operator=(const complex_signal& other) {
      return *this = complex_signal(other);
    }

    complex_signal& operator=(complex_signal&& other) throw() {
      std::swap(signal, other.signal);
      return *this;
    }

    complex_signal& operator+=(const duration& d) {
      signal += d;
      return *this;
    }

    complex_signal& operator++() {
      ++signal;
      return *this;
    }

    complex_signal operator++(int) {
      complex_signal ret{*this};
      ++*this;
      return ret;
    }

    sample operator*() const  {
      return complex_of<typename signal_traits<Signal>
			::sample>::convert(*signal);
    }

  };

  template <typename Signal>
  struct signal_traits<complex_signal<Signal> > {
    typedef typename complex_of<typename signal_traits<Signal>
				::sample>::type sample;
    typedef typename signal_traits<Signal>::duration duration;
  };

  template <typename T, typename RT = typename std::decay<T>::type,
	    typename = typename signal_traits<RT>::sample>
  complex_signal<RT> complex_value(T&& t) {
    return complex_signal<RT>{std::forward<T>(t)};
  }
}

#endif

